package com.travel.Config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.travel.Filter.LoginFilter;
import com.travel.Service.Impl.ManagerServiceImpl;
import com.travel.util.R;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.authentication.AuthenticationProvider;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private ManagerServiceImpl managerService;

/*    @Autowired
    private Security_PasswordEncoder passwordEncoderl;*/

    @Autowired
    private DataSource dataSource;

    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private SecuritySuccessHandler securitySuccessHandler;


    public void configure(WebSecurity wed){
        wed.ignoring().antMatchers("/PayYes","/PayNo","/Ticket/AliPayTest","/Ticket/AliPayNotify","/Test/**"
                ,"/static/**","/css","/images","/js","/lib","/api","/swagger-ui.html");
    }

    protected void configure(HttpSecurity http) throws Exception{

        http.csrf().disable().addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class);


        //对指定文件或文件夹设置指定权限，不设置即统一放行
/*        http.authorizeRequests()

                .antMatchers("/Right/**","/Love").permitAll()
                .antMatchers("/Admin/**").permitAll()
                .antMatchers("/Index","/Welcome").hasAnyRole("user","admin")
                .antMatchers("/File/**","/Friend").hasAnyRole("user","admin")

                .anyRequest().authenticated()
                .and()
                .sessionManagement()
                .maximumSessions(2)
                .expiredUrl("/userLogin")
                ;*/


        http


                .authenticationProvider(authenticationProvider())

                .httpBasic()
                //未登录时，进行json格式的提示，很喜欢这种写法，不用单独写一个又一个的类
                .authenticationEntryPoint((request,response,authException) -> {
                    response.setContentType("application/json;charset=utf-8");
                    response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                    PrintWriter out = response.getWriter();
                    Map<String,Object> map = new HashMap<String,Object>();
                    map.put("code",403);
                    map.put("message","未登录");
                    out.write(objectMapper.writeValueAsString(map));
                    out.flush();
                    out.close();
                })

                .and()
                .authorizeRequests()
                .antMatchers("/webjars/**","/static/**","/uploads/**").permitAll()
                .antMatchers("/css","/images","/js","/lib","/api","/UploadFile").permitAll()
//                .antMatchers("/PayYes","/PayNo","/Ticket/AliPayTest","/Ticket/AliPayNotify").permitAll()
                .antMatchers("/SuperPage/**").hasAnyRole("super","admin")
                .antMatchers("/SuperPage/sIndex").hasAnyRole("super","admin")
                .antMatchers("/AdminCulture/**").hasAnyRole("super")
                .anyRequest().authenticated() //必须授权才能范围

                .and()
                .formLogin() //使用自带的登录
                .permitAll()
                //登录失败，返回json
                .failureHandler((request,response,ex) -> {
                    response.setContentType("application/json;charset=utf-8");
                    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                    PrintWriter out = response.getWriter();
                    Map<String,Object> map = new HashMap<String,Object>();
                    map.put("code",401);
                    if (ex instanceof UsernameNotFoundException || ex instanceof BadCredentialsException) {
                        map.put("message","用户名或密码错误");
                    } else if (ex instanceof DisabledException) {
                        map.put("message","账户被禁用");
                    } else {
                        map.put("message","登录失败!");
                    }
                    out.write(objectMapper.writeValueAsString(map));
                    out.flush();
                    out.close();
                })
                //登录成功，返回json
                .successHandler((request,response,authentication) -> {
                    Map<String,Object> map = new HashMap<String,Object>();
                    map.put("code",200);
                    map.put("message","登录成功");
                    map.put("data",authentication);
                    response.setContentType("application/json;charset=utf-8");
                    PrintWriter out = response.getWriter();
                    out.write(objectMapper.writeValueAsString(map));
                    out.flush();
                    out.close();
                })
                .and()
                .exceptionHandling()
                //没有权限，返回json
                .accessDeniedHandler((request,response,ex) -> {
                    response.setContentType("application/json;charset=utf-8");
                    response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                    PrintWriter out = response.getWriter();
                    Map<String,Object> map = new HashMap<String,Object>();
                    map.put("code",403);
                    map.put("message", "权限不足");
                    out.write(objectMapper.writeValueAsString(map));
                    out.flush();
                    out.close();
                })
                .and()
                .logout()
                //退出成功，返回json
                .logoutSuccessHandler((request,response,authentication) -> {
                    Map<String,Object> map = new HashMap<String,Object>();
                    map.put("code",200);
                    map.put("message","退出成功");
                    map.put("data",authentication);
                    response.setContentType("application/json;charset=utf-8");
                    PrintWriter out = response.getWriter();
                    out.write(objectMapper.writeValueAsString(map));
                    out.flush();
                    out.close();
                })
                .permitAll();


        //开启跨域访问
        http.cors().disable();
        //开启模拟请求，比如API POST测试工具的测试，不开启时，API POST为报403错误
        http.csrf().disable();


            //设置指定指定的登录页及登录页给后台传输的指定name
        http.formLogin()

//                .loginPage("/userLogin").permitAll()
                //账号和密码的Parameter是登录页面中input标签的name属性
                .usernameParameter("username").passwordParameter("password")
//                .defaultSuccessUrl("/SuperPage/sIndex",true)
                .successHandler(securitySuccessHandler)
//                .successForwardUrl("/Index")

//                .failureUrl("/userLogin?error");
            ;

//        http.authorizeRequests();
        http.headers().frameOptions().sameOrigin();

        http.rememberMe()

                .rememberMeParameter("rememberme")
                .tokenValiditySeconds(2 * 24 * 60 * 60)
                .tokenRepository(tokenRepository());


        http.exceptionHandling().accessDeniedPage("/403");


        http.logout()
                .logoutUrl("/mylogout")
                .logoutSuccessUrl("/");



    }


    protected void configure(AuthenticationManagerBuilder auth) throws Exception{

        //配置使用数据库中的账户、密码
        auth.userDetailsService(managerService).passwordEncoder(passwordEncoder());

    /*    auth.inMemoryAuthentication().
                withUser("admin").password("{noop}admin").roles("admin")
                .and()
                .withUser("user").password("{noop}user").roles("user");*/
    }

    @Bean
    public PasswordEncoder passwordEncoder(){

        //使用自定义的加密算法
        return new Security_PasswordEncoder();
    }

    public AuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        //对默认的UserDetailsService进行覆盖
        authenticationProvider.setUserDetailsService(managerService);
        authenticationProvider.setPasswordEncoder(passwordEncoder());
        return authenticationProvider;
    }


    @Bean
    public JdbcTokenRepositoryImpl tokenRepository(){
        JdbcTokenRepositoryImpl jr=new JdbcTokenRepositoryImpl();
        jr.setDataSource(dataSource);
        return jr;
    }


    LoginFilter loginFilter() throws Exception {
        LoginFilter loginFilter = new LoginFilter();
        // 登录成功的回调
        loginFilter.setAuthenticationSuccessHandler((request, response, authentication) -> {
            response.setContentType("application/json;charset=utf-8");
            PrintWriter out = response.getWriter();
            R ok = R.ok("登录成功!", authentication.getPrincipal().toString(),1L);
            String s = new ObjectMapper().writeValueAsString(ok);
            out.write(s);
            out.flush();
            out.close();
        });
        // 登录失败
        loginFilter.setAuthenticationFailureHandler((request, response, exception) -> {
            response.setContentType("application/json;charset=utf-8");
            PrintWriter out = response.getWriter();
            R ResponseCommon = R.no(exception.getMessage());
            ResponseCommon.setMsg("登录错误!");
            out.write(new ObjectMapper().writeValueAsString(ResponseCommon));
            out.flush();
            out.close();
        });
        loginFilter.setAuthenticationManager(authenticationManagerBean());
        loginFilter.setFilterProcessesUrl("/doLogin");
        return loginFilter;
    }




}
